﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using System.Reflection;
using Microsoft.Practices.Mobile.ContainerModel;

namespace EditUsers_Best
{
    public static class ApplicationRoot
    {
        static ApplicationRoot()
        {
            Container = new Container();
        }

        /// <summary>
        /// Exposes the root application container where global 
        /// services are registered.
        /// </summary>
        /// <devdoc>
        /// The internal setter allows a test project to change 
        /// the container with a test one (need to enable 
        /// InternalsVisibleToAttribute at the assembly level).
        /// </devdoc>
        public static Container Container { get; internal set; }

        /// <summary>
        /// Initializes the root container.
        /// </summary>
        public static void Initialize()
        {
            // Create object to build the root container
            var container = new Container();

            // Register the TaskService - the defaults are used which means it is a
            // singleton with global scope. The first time any module needs a reference to
            // the ITaskService (i.e. they call 'container.Resolve<ITaskService>()') then
            // the container will build one using the function defined in the arguments to 
            // the Register<TService> call:
            //'(c => new TaskService(c));' 
            //
            // What does that mean?
            // If you look at the ContainerBuilder.Register<TService> function definition in the source code
            // you find that it is defined as:
            // 		public IRegistration<TService> Register<TService>(Func<Container, TService> factory)
            // The argument is of type Func<Container, TService>, which means a function that 
            // takes one argument of type 'Container' and returns a 'TService' (TService is the type you 
            // supply when calling the function).
            // 'c => new TaskService(c)' is a lambda function, which means you are defining an
            // anonymous method that takes one argument 'c' and the body of the method contains the code
            // (return) new TaskService(c).
            // We know from the definition of the Register<TService>() method that the type of that argument
            // to the factory function is actually 'Container', so from this we understand that 
            // 'c => new TaskService(c)' is actually shorthand for a factory function that looks like this:
            //
            // ITaskService myAnonymousFactoryFn(Container c)
            // {
            //    return new TaskService(c);
            // }
            container.Register<ITaskService>(c => new TaskService(c));

            // Register the MainPresenter, the constructor of which takes arguments of
            // a MainForm instance and the ITaskService instance.
            // This is an example of using 'Constructor injection'
            container.Register(
                c => new MainPresenter(
                    c.Resolve<MainForm>(),
                    c.Resolve<ITaskService>()));

            // Register the MainForm type and its construction function which takes no arguments.
            // Also define an Initialisation function that the container must call after construction.
            // In this case, the init function sets the Presenter property of the new MainForm 
            // instance - a good exampole of using 'Setter injection'
            container
                .Register(c => new MainForm())
                .InitializedBy((c, v) => v.Presenter = c.Resolve<MainPresenter>());

            // Save our container instance
            Container = container;

            // Now call the AppConfigurator which will call the configurator
            // method of all other modules in the application, in which they will
            // register their own types with the container, along with their factory functions.
            new AppConfiguration.ConfigurationModule().Configure(Container);
        }
    }
}
